/////////////////////////////////////////////////////////////////////////////////////
// Include Files
#include "Rendering\RenderState.h"

// managers
#include "Managers\TextureManager.h"
#include "Managers\ShaderManager.h"
#include "Managers\MeshManager.h"

/////////////////////////////////////////////////////////////////////////////////////
// CRenderState Implementation
CRenderState::CRenderState() : 
	m_Mutex(), 
	m_matTransform(), 
	m_strMesh(), 
	m_strShader(), 
	m_lstMatParams(), 
	m_lstTexParams(), 
	m_uRefCount(0)
{ }

void CRenderState::Render( matrix_t const &_matView, matrix_t const &_matProj )
{
#pragma warning(disable:981)

	// HACK: transform is currently the only element which changes
	Lock();
	matrix_t matModelView = MatrixMul(m_matTransform, _matView);
	Unlock();

	matrix_t matModelViewProj = MatrixMul(matModelView, _matProj);

	matrix_t matModelViewRot = matModelView;
	matModelViewRot.row[3] = Vector(0.0f, 0.0f, 0.0f, 1.0f);

	matrix_t matModelViewProjRot = MatrixMul(matModelViewRot, _matProj);

	// get the technique
	CGtechnique tech = (CGtechnique)m_pShaderManager->GetTechnique(m_strShader);
	assert(tech);

	// set the matrix parameters
	for( uint_t i = 0; i < m_lstMatParams.GetSize(); ++i )
	{
#ifdef _DEBUG
		GLenum eError;
#endif

		CGparameter param = (CGparameter)m_pShaderManager->GetParameter(m_strShader, m_lstMatParams[i].strParam);
		assert(param);

#ifdef _DEBUG
		eError = glGetError();
		assert(eError == GL_NO_ERROR);
#endif

		switch(m_lstMatParams[i].eType)
		{

		case PARAM_MAT_MODELVIEW:
			cgGLSetMatrixParameterfr(param, matModelView.arr);
			break;

		case PARAM_MAT_MODELVIEWPROJ:
			cgGLSetMatrixParameterfr(param, matModelViewProj.arr);
			break;

		case PARAM_MAT_MODELVIEW_ROT:
			cgGLSetMatrixParameterfr(param, matModelViewRot.arr);
			break;

		case PARAM_MAT_MODELVIEWPROJ_ROT:
			cgGLSetMatrixParameterfr(param, matModelViewProjRot.arr);
			break;

		}

#ifdef _DEBUG
		eError = glGetError();
		assert(eError == GL_NO_ERROR);
#endif
	}

	// set texture parameters
	for( uint_t iTex = 0; iTex < m_lstTexParams.GetSize(); ++iTex )
	{
		TTexture    *pTex   = &m_lstTexParams[iTex];

		CGparameter TexParam = (CGparameter)m_pShaderManager->GetParameter(m_strShader, pTex->strParam);
		uint_t      uiTex    = m_pTextureManager->GetTexture(pTex->strName);

		cgGLSetupSampler(TexParam, uiTex);
	}

	// get the mesh from the mesh manager
	TMesh *pMesh = m_pMeshManager->GetMesh(m_strMesh);

	// bind the vertex array object
	glBindVertexArray(pMesh->uiVAO);

	GLenum eError = glGetError();
	if( eError != GL_NO_ERROR )
	{
		assert(CE_FALSE);
		// TODO: check error code
	}

	// loop through passes
	CGpass pass = cgGetFirstPass(tech);
	while(pass)
	{
		cgSetPassState(pass);

		// draw the mesh
		glDrawElements(GL_TRIANGLES, pMesh->uiIndexCount,
			pMesh->eIndexType, (void *)0 );

		cgResetPassState(pass);
		pass = cgGetNextPass(pass);
	}

	eError = glGetError();
	if( eError != GL_NO_ERROR )
	{
		assert(CE_FALSE);
		// TODO: check error code
	}

#pragma warning(default:981)
}
